/**
 * 
 */
package research.sbookmarking.io;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import org.afox.util.RMethod;
import org.connotea.Bookmark;
import org.connotea.Post;
import org.connotea.Tag;
import org.restlet.Client;
import org.restlet.data.Method;
import org.restlet.data.Protocol;
import org.restlet.data.Reference;
import org.restlet.data.Request;
import org.restlet.data.Response;

import research.sbookmarking.DataContainer;
import research.sbookmarking.io.xml.XmlController;


/**
 * This class controller for getting input, where it the commands to execute
 * against Connotea.org or retrieving the data from Connotea.org
 * <p>
 * 
 * @author <A HREF="mailto:aatleung@gmail.com">Albert Leung</a>
 * @version 1.0
 */
public class InputController {

	private HashMap<String,InputStreamFactory> factoryMap = new HashMap<String,InputStreamFactory>();

	private static InputController anInstance = null;
	private String url = null;
	private String username = null;

	/**
	 * The only way to instantiate this class is through
	 * {@link #getInstance() getInstance}
	 */
	private InputController() {
		// TODO Auto-generated constructor stub
		factoryMap.put("InputStream",InputStreamFactory.getInstance());
		factoryMap.put("DataInputStream", DataInputStreamFactory.getInstance());
		factoryMap.put("ObjectInputStream",ObjectInputStreamFactory.getInstance());
	}

	/**
	 * Ensures that there is only one InputController instance. If an instance
	 * already exists it return the address of that instance otherwise it will
	 * instantiate an instance and save it.
	 * <p>
	 * 
	 * @return an instance of this object.
	 */

	public static InputController getInstance() {
		if (anInstance == null) {
			anInstance = new InputController();
		}

		return anInstance;
	}

	public void setURL (String url){
		this.url = url;
	}

	public void setUsername (String username){
		this.username = username;
	}
	
	/**
	 * This method calls the appropriate method to extract the commands to be
	 * executed against Connotea from the given filename.
	 * <p>
	 * 
	 * @param filename
	 *            contains the name of the file that contains the commands to be
	 *            executed against Connotea.
	 * @return a list of {@link DataContainer DataContainer} where each
	 *         container contains the results from querying of Connotea.
	 */
	public List<DataContainer> getQuery(String filename) {
		XmlController controller = new XmlController(filename);
		ArrayList<DataContainer> results = new ArrayList<DataContainer>();
		Iterator<Command> commands = controller.getCommands().iterator();

		while (commands.hasNext()) {
			Command command = commands.next();
			command.execute();
			results.add(command.getResult());
		}

		return results;
	}
	
	private BufferedReader getStream(String uri) throws UnknownHostException, IOException{
/*
		PipedInputStream in = new PipedInputStream();
		PipedOutputStream out = new PipedOutputStream(in);
		
        final Client client = new Client(Protocol.HTTP);
        // Instantiates a request with a method and the resource's URI
        final Request request = new Request(Method.GET, uri);

        // Sends the request and gets the response
        final Response response = client.handle(request);
        // Prints the status of the response
        System.out.println(response.getStatus());
        // Writes the response's entity content, if available
        response.getEntity().write(out);
        out.flush();
*/
        return new BufferedReader(new InputStreamReader((new Socket(uri,80)).getInputStream()));
	}
	
	private void parse (BufferedReader in, String input, List<Post> posts) throws IOException, ParseException{
		String date;
		Post aPost = null;
		
		System.out.println("input: "+input);
		if (input.contains("span title=")){
			date =input.substring(input.indexOf('\"')+1);
			do {
				input=in.readLine();
				if(input.contains("div class=\"data\"")){
					if (aPost!=null){
						aPost.setBookmark(new Bookmark(new Reference(url)));
						aPost.setCreator(username);
						posts.add(aPost);
					}
					aPost = new Post();
					SimpleDateFormat format = new SimpleDateFormat("d MMM yy");
					aPost.setCreated(format.parse(date));
				}
				else if (input.contains("<div class=\"description\">")){
					StringBuffer temp = new StringBuffer();
					input=in.readLine();
					while (input!=null && input.contains("</div>")){
						temp.append(input.trim());
						input=in.readLine();
					}
					aPost.setDescription(temp.toString());
				}
				else if (input.contains("<a rel=\"nofollow\" class=\"taggedlink\" href=")){
					String temp = input.replace("<a rel=\"nofollow\" class=\"taggedlink\" href=\"","");
					url = temp.substring(0,temp.indexOf('\"'));
					aPost.setTitle(input.substring(input.indexOf(">")+1,input.indexOf("</a>")));
				}
				else if (input.contains("class=\"user user-tag\"><span class=\"\">")){
					String temp = input.replace("</span></a>","");
					username = temp.substring(temp.lastIndexOf('>')+1);
				}
				else if (input.contains("<span class=\"tag-chain-item-span\">")){
					List<Tag> tags = new ArrayList<Tag>();
					
					do {
						String temp = input.replace("</span></a></li>","");
						tags.add(new Tag(temp.substring(temp.lastIndexOf('>'))));
						input = in.readLine();
					} while (input!=null && input.contains("</div>"));
				}
			}while(input!=null && input.contains("<script>")!=true);
			if (input!=null){
				aPost.setBookmark(new Bookmark(new Reference(url)));
				aPost.setCreator(username);
				posts.add(aPost);
			}
		}
	}
	
	public List<Post> getPosts (String uri) throws UnknownHostException, IOException, ParseException{
		BufferedReader br = getStream(uri);
		List<Post> posts = new ArrayList<Post>();
		
		String temp = br.readLine();
		
		while (temp!=null){
			parse(br, temp,posts);
			temp = br.readLine();
		} 
		
		return posts;
	}

	/**
	 * The method executes the commands that was retrieved from the method
	 * {@link #getCommands(String) getCommands}
	 * <p>
	 * 
	 * @param commands
	 *            a list of commands to be executed.
	 * @return the data in the {@link DataContainer DataContainer} object.
	 */
//	private DataContainer getData(ArrayList commands) {
//		Iterator items = commands.iterator();
//
//		while (items.hasNext()) {
//			Command aComment = (Command) items.next();
//			aComment.execute();
//		}
//
//		return packageData(commands);
//	}

	/**
	 * It extracts the data collected and combines it into a DataContainer
	 * object.
	 * <p>
	 * 
	 * @param aList
	 *            ArrayListdata
	 * @return the data in the {@link DataContainer DataContainer} object.
	 */

/*
	public List<Object> retrievedSavedData(List list, String filename) {
		return null;
	}
*/
	private InputStreamFactory getFactory(String factory){
		InputStreamFactory temp = factoryMap.get(factory);
		
		if (temp == null){
			throw new NullPointerException (factory + " have not been created yet.");
		}
		
		return temp;
	}
	
	public InputStream getInputStream (String filename, String factory){
		return getFactory(factory).getInputStream(filename);
	}
	
	public List<String> readQueryList (String filename, String factory) {
		ArrayList<String> queryList = null;
		
		try {
			queryList = new ArrayList<String>();
			BufferedReader file = getFactory(factory).getFileReader(filename);
			
			String temp;
			while ((temp = file.readLine())!=null){
				queryList.add(temp);
			}
			
			closeStream(factory,filename);
		}
		catch (IOException e){
			throw new RuntimeException(e);
		}
		
		return queryList;
	}
	
	public void closeStream(String factory, String filename){
		InputStreamFactory isf = getFactory(factory);
		Object stream = isf.getStream(filename);
		RMethod.invoke(stream,"close",new ArrayList<Object>());
		isf.removeStream(filename);
	}

	public static void main(String[] args) {
		// System.out.println (((new Object()).getClass()).getSimpleName());
		// System.out.println ("output: "
		// +InputController.getInstance().getQuery("setting.xml"));
		// System.out.println("size: "
		//]		+ InputController.getInstance().getQuery("setting.xml").size());
	}

}
